import { Context } from 'koa';
import Router from 'koa-router';
import { join, parse } from 'path';
import * as fs from 'fs';
import RouteConfig from '/Config/Route';
import BaseConfig from '/Config/Base';
import GlobalMiddleware from '/App/middleware';


// 使用路由
const router = new Router();
/**创建上传缓存文件目录 */
if (BaseConfig?.koaBody?.formidable?.uploadDir) fs.mkdirSync(BaseConfig.koaBody.formidable.uploadDir, { recursive: true });

/**
 * 注入路由当前调用的 控制器和类
 * @param newObjClass 控制器类对象
 * @param funName 方法名
 * @param filePath 当前控制器文件路径
 * @param Strong 是否强路由
 * @returns 
 */
function ParseReqPath(newObjClass: any, funName: string, filePath: string, Strong: boolean = true) {

    const className = newObjClass?.constructor?.name ?? '';

    return async (ctx: Context, next: Function) => {
        ctx.isRouteStrong = Strong;//添加强路由标识
        // 注入当前路由请求路径
        const filePathParse = parse(filePath);
        ctx.reqPath = {
            controller: className,
            apiName: funName,
            filePath: join(filePathParse.dir, filePathParse.name),
            ext: filePathParse.ext//文件后缀
        };
        await next();
    }

}

/**
 * (字符串)处理控制器的方法调用和返回值处理(强路由才会调用这个)
 * @param newObjClass 类
 * @param Param 细节参数（访问的路径等）
 * @returns 
 */
function Settlement(newObjClass: any, Param: Record<string, any>) {
    return async (ctx: Context) => {
        const apiFunc = newObjClass[Param.method];
        if (typeof apiFunc != 'function') {
            const className = newObjClass.constructor?.name ?? '';
            ctx.throw(404, `${className} 下的 ${Param.method} 方法不存在！`);
        }
        await bodyParser(apiFunc(ctx, ctx.params), ctx);
    }
}

/**
 * (方法)处理控制器的方法调用和返回值处理
 * @param point 方法
 * @returns 
 */
function SettlementFunc(point: Function) {
    return async (ctx: Context) => {
        if (typeof point != 'function') {
            let errorMsg = `${ctx.reqPath.controller} 下的 ${ctx.reqPath.apiName}`;
            if (ctx.isRouteStrong) {
                let ctxPath = ctx.path;
                if (ctxPath.length > 30) ctxPath = `${ctxPath.slice(0, 26)}...${ctxPath.slice(26, 31)}`;
                errorMsg = `${ctxPath} 强路由指定的`;
            }
            ctx.throw(404, `${errorMsg},方法不存在！`);
        }
        await bodyParser(point(ctx, ctx.params), ctx);
    }
}

/**强路由配置类 */
export class Route {
    /**
     * 新增一条get路由
     * @param rule 路由规则
     * @param point 指向的函数或类
     */
    static get(rule: string, point: string | ((ctx: Context) => void)) {
        RequestWrapper(rule, point, 'get');
        return Route;
    }
    /**
     * 新增一条post路由
     * @param rule 路由规则
     * @param point 指向的函数或类
     */
    static post(rule: string, point: string | ((ctx: Context) => void)) {
        RequestWrapper(rule, point, 'post');
        return Route;
    }
    /**
     * 新增一条put路由
     * @param rule 路由规则
     * @param point 指向的函数或类
     */
    static put(rule: string, point: string | ((ctx: Context) => void)) {
        RequestWrapper(rule, point, 'put');
        return Route;
    }
    /**
     * 新增一条delete路由
     * @param rule 路由规则
     * @param point 指向的函数或类
     */
    static delete(rule: string, point: string | ((ctx: Context) => void)) {
        RequestWrapper(rule, point, 'delete');
        return Route;
    }
    /**
     * 新增一条任意请求路由
     * @param rule 路由规则
     * @param point 指向的函数或类
     */
    static all(rule: string, point: string | ((ctx: Context) => void)) {
        RequestWrapper(rule, point, 'all');
        return Route;
    }
    /**
     * 新增一条重定向路由
     * @param rule 路由规则
     * @param newRoute 指向的路由
     */
    static redirect(rule: string, newRoute: string) {
        router.redirect(rule, newRoute);
        return Route;
    }
}


/**
 * 请求包装函数
 * @param rule 路由规则
 * @param point 路由指向的函数或类
 * @param RequestWay 请求方式
 * @returns 
 */
function RequestWrapper(rule: string, point: string | ((ctx: Context) => void), RequestWay: string): boolean {
    if (point instanceof Function) {
        (router as any)[RequestWay](rule, ParseReqPath(null, '', ''), GlobalMiddleware, SettlementFunc(point));
    } else {
        const { dir, name: method } = parse(point);
        let controllerPath = join(__dirname, '../App/controller', dir);
        const suffixs = ['.ts', '.js', '.jsc'];
        const isIndex = suffixs.findIndex(suffix => fs.existsSync(`${controllerPath}${suffix}`));
        if (isIndex === -1) {
            logger.warn(`强路由-控制器[${controllerPath}]不存在`);
            return false;
        }
        controllerPath = `${controllerPath}${suffixs[isIndex]}`;

        const { default: newObj } = require(controllerPath);
        if (typeof newObj?.constructor != 'function') {
            logger.warn(`强路由-控制器[${controllerPath}]不是一个类`);
            return false;
        };
        const newObjClass = new newObj();
        // 如果中间件配置不是数组就设置数组
        if (!Array.isArray(newObjClass._middleware)) newObjClass._middleware = [];
        if (typeof newObjClass[method] !== 'function') {
            logger.error(`强路由-控制器[${controllerPath}]下的[${method}]方法不存在！`);
            if (process.env.NODE_ENV !== 'development') {
                return false;
            }
        }


        (router as any)[RequestWay](rule, ParseReqPath(newObjClass, method, dir), GlobalMiddleware, ...newObjClass._middleware ?? [], Settlement(newObjClass, {
            method,
            path: dir,
            controlle: parse(dir).name
        }));
    }
    return true;
}



/**
 * 判断是不是Promise对象
 * @param {Any} obj
 * @returns
 */
function isPromise(obj: any): boolean {
    try {
        return obj instanceof Promise;
    } catch (error) {
        return false;
    }
}

/**
 * body数据处理
 * @param results 
 * @param ctx 
 */
async function bodyParser(results: any, ctx: Context) {
    if (isPromise(results)) {
        let resuls = await results;
        if (typeof resuls == 'undefined' && typeof ctx.body == 'undefined') {
            ctx.body = '';
        } else if (resuls) {
            ctx.body = resuls;
        }
    } else {
        if (typeof results == 'undefined' && typeof ctx.body == 'undefined') {
            ctx.body = '';
        } else if (results) {
            ctx.body = results;
        }
    }
}


function isSet(obj: any): boolean {
    return Object.prototype.toString.call(obj) === '[object Set]';
}

/**
 * 递归遍历控制器文件夹
 * @param {String} path 路径
 */
function eachController(path: string, callback: ((path: string, fileName: string) => any)) {
    const fsList: Array<fs.Dirent> = fs.readdirSync(path, { withFileTypes: true });
    for (const file of fsList) {
        if (!file.isFile()) {
            // 文件夹
            let NewPath = path + '/' + file.name;
            eachController(NewPath, callback);//继续递归
        } else if (/.*\.(js|ts|jsc)$/i.test(file.name)) {
            // ts|js文件
            let fileName = parse(file.name).name;//解析移除后缀
            callback(path, fileName);//回调函数
        }
    }
}

/**
 * 初始化注册路由
 */
export function initRoute() {

    /**
     * 动态路由注册(Reinforce是true就不注册动态路由)
     */
    if (!RouteConfig.Reinforce) {
        // 控制器最初的路径
        const initPath = join(__dirname, '../App/controller/');

        // 注册动态路由(请求类型判断分类)
        function routeTypeIf(RouterRule: string, middlewares: Array<Function>, newObjClass: any, funName: string, filePath: string) {
            let method = newObjClass._method ?? [];
            // 如果是数组转set去重
            if (Array.isArray(method)) method = Array.from(new Set([...method]));
            if (isSet(method)) method = Array.from(method);
            const reqFilePath = filePath.replace(/.*App\\controller(.+)$/i, '$1').replace(/\\/g, '/');// 获取到控制器的文件路径
            if (Array.isArray(method) && method.length > 0) {
                // 配置了请求方法列表 (遍历请求方法来实现)
                method.forEach((m: string) => (router as any)[m](RouterRule, ParseReqPath(newObjClass, funName, reqFilePath, false), GlobalMiddleware, ...middlewares, SettlementFunc(newObjClass[funName])));
                return;
            }
            (router as any)['all'](RouterRule, ParseReqPath(newObjClass, funName, reqFilePath, false), GlobalMiddleware, ...middlewares, SettlementFunc(newObjClass[funName]));// 注册路由
        }

        // 遍历并注册控制器动态路由
        eachController(initPath, (path, fileName) => {
            let filePath = join(path, fileName);
            const suffixs = ['.ts', '.js', '.jsc'];
            const isIndex = suffixs.findIndex(suffix => fs.existsSync(`${filePath}${suffix}`));
            if (isIndex === -1) {
                logger.warn(`动态路由-控制器[${filePath}]不存在`);
                return;
            }
            filePath = `${filePath}${suffixs[isIndex]}`;

            try {
                const { default: newObj } = require(filePath);
                if (typeof newObj?.constructor != 'function') {
                    logger.warn(`动态路由-控制器[${filePath}]不是一个类`);
                    return false;
                };
                const newObjClass = new newObj();
                // 如果中间件配置不是数组就设置数组
                if (!Array.isArray(newObjClass._middleware)) newObjClass._middleware = [];
                // 获取类的方法列表
                const methodNames = Object.getOwnPropertyNames(Object.getPrototypeOf(newObjClass)).filter(name => name != 'constructor');//返回类的所有方法(不包含 constructor )
                let Directory = path.replace(initPath, '');// 路由目录


                for (const funName of methodNames) {
                    const RouterRule = join('/', Directory, fileName, funName).replace(/\\/g, '/');// 路由规则
                    routeTypeIf(RouterRule, newObjClass._middleware, newObjClass, funName, filePath);

                    // 注册控制器方法入口路由
                    if (RouteConfig.DefaultAction == funName) {
                        const RouterRule = join('/', Directory, fileName).replace(/\\/g, '/');// 路由规则
                        routeTypeIf(RouterRule, newObjClass._middleware, newObjClass, funName, filePath);
                    }

                }
            } catch (error) {
                logger.error(`动态路由-控制器[${filePath}]注册失败`, error);
            }

        });
    }



    /**
     * 加载自定义强路由
     */
    // 强路由的路径
    const strongPath = join(__dirname, '../Routes/');
    eachController(strongPath, (path, fileName) => require(`${path}/${fileName}`));
}

export default router;